package org.dizena.quartz.service;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dizena.common.interaction.request.PageRequest;
import org.dizena.quartz.adapter.job.GroupJob;
import org.dizena.quartz.adapter.job.SingleJob;
import org.dizena.quartz.bean.cmd.AddGroupJobCmd;
import org.dizena.quartz.bean.cmd.AddSingleJobCmd;
import org.dizena.quartz.bean.cmd.JobCmd;
import org.dizena.quartz.bean.dto.QuartzJobDetail;
import org.dizena.quartz.dao.QuartzJobDetailMapper;
import org.quartz.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class QuartzJobService {
    @Resource
    private Scheduler scheduler;
    @Resource
    private QuartzJobDetailMapper detailMapper;


    public void addSingleJob(AddSingleJobCmd cmd) {
        try {
            // Job 数据 trigger数据注意设计的时候区分好
            JobDataMap jobData = new JobDataMap();
            jobData.put("url", cmd.getUrl());
            jobData.put("method", cmd.getMethod());

            JobDataMap triggerData = new JobDataMap();
            if (cmd.getParam() != null && !cmd.getParam().isEmpty()) {
                triggerData.putAll(cmd.getParam());
            }

            JobDetail jobDetail = JobBuilder.newJob(SingleJob.class)
                    .withIdentity(cmd.getName(), cmd.getGroup())
                    .withDescription(cmd.getDescription())
                    .usingJobData(jobData)
                    .build();

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(cmd.getName(), cmd.getGroup())
                    .withSchedule(CronScheduleBuilder.cronSchedule(cmd.getCron()))
                    .usingJobData(triggerData)
                    .build();

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            log.error("创建任务失败", e);
        }
    }

    public void updateSingleJob(AddSingleJobCmd cmd) {
        try {
            // Job 数据 trigger数据注意设计的时候区分好
            JobDataMap jobData = new JobDataMap();
            jobData.put("url", cmd.getUrl());
            jobData.put("method", cmd.getMethod());

            JobDataMap triggerData = new JobDataMap();
            if (cmd.getParam() != null && !cmd.getParam().isEmpty()) {
                triggerData.putAll(cmd.getParam());
            }

            TriggerKey triggerKey = TriggerKey.triggerKey(cmd.getName(), cmd.getGroup());
            JobKey jobKey = JobKey.jobKey(cmd.getName(), cmd.getGroup());

            // 暂停的任务无法修改
            Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
            if (state == Trigger.TriggerState.PAUSED) {
                scheduler.resumeJob(jobKey);
            }

            //更新job detail
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            jobDetail = JobBuilder.newJob(jobDetail.getJobClass())
                    .withIdentity(jobKey)
                    .withDescription(cmd.getDescription())
                    .usingJobData(jobData)
                    .storeDurably(true)
                    .build();

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (cmd.getCron() != null) {
                // 表达式调度构建器
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder()
                        .withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(cmd.getCron()))
                        .forJob(jobDetail)
                        .usingJobData(triggerData)
                        .build();
            }

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);

            if (state == Trigger.TriggerState.PAUSED) {
                scheduler.pauseJob(jobKey);
            }
        } catch (Exception e) {
            log.error("更新任务失败", e);
        }
    }

    public void addGroupJob(AddGroupJobCmd cmd) {
        try {
            // Job 数据 trigger数据注意设计的时候区分好
            JobDataMap triggerData = new JobDataMap();
            triggerData.put("children", JSON.toJSONString(cmd.getChildren()));

            // Job 数据 trigger数据注意设计的时候区分好
            JobDetail jobDetail = JobBuilder.newJob(GroupJob.class)
                    .withIdentity(cmd.getName(), cmd.getGroup())
                    .withDescription(cmd.getDescription())
                    .build();

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(cmd.getName(), cmd.getGroup())
                    .withSchedule(CronScheduleBuilder.cronSchedule(cmd.getCron()))
                    .usingJobData(triggerData)
                    .build();

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            log.error("创建任务失败", e);
        }
    }

    public void updateGroupJob(AddGroupJobCmd cmd) {
        try {
            // Job 数据 trigger数据注意设计的时候区分好
            JobDataMap triggerData = new JobDataMap();
            triggerData.put("children", JSON.toJSONString(cmd.getChildren()));

            TriggerKey triggerKey = TriggerKey.triggerKey(cmd.getName(), cmd.getGroup());
            JobKey jobKey = JobKey.jobKey(cmd.getName(), cmd.getGroup());

            // 暂停的任务无法修改
            Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
            if (state == Trigger.TriggerState.PAUSED) {
                scheduler.resumeJob(jobKey);
            }

            //更新job detail
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            jobDetail = JobBuilder.newJob(jobDetail.getJobClass())
                    .withIdentity(jobKey)
                    .withDescription(cmd.getDescription())
                    .storeDurably(true)
                    .build();

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (cmd.getCron() != null) {
                // 表达式调度构建器
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder()
                        .withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(cmd.getCron()))
                        .forJob(jobDetail)
                        .usingJobData(triggerData)
                        .build();
            }

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);

            if (state == Trigger.TriggerState.PAUSED) {
                scheduler.pauseJob(jobKey);
            }
        } catch (Exception e) {
            log.error("更新任务失败", e);
        }
    }

    public Page<QuartzJobDetail> listJob(PageRequest qry) {
        QueryWrapper<QuartzJobDetail> q = new QueryWrapper<>();
        Page<QuartzJobDetail> result = detailMapper.selectPage(Page.of(qry.getPage(), qry.getSize()),q);
        return result;
    }

    public void pauseJob(JobCmd cmd) {
        try {
            scheduler.pauseJob(JobKey.jobKey(cmd.getName(), cmd.getGroup()));
        } catch (SchedulerException e) {
            log.error("暂停任务失败", e);
        }
    }


    public void resumeJob(JobCmd cmd) {
        try {
            scheduler.resumeJob(JobKey.jobKey(cmd.getName(), cmd.getGroup()));
        } catch (SchedulerException e) {
            log.error("恢复任务失败", e);
        }
    }


    public void runNow(JobCmd cmd) {
        try {
            scheduler.triggerJob(JobKey.jobKey(cmd.getName(), cmd.getGroup()));
            //todo 另外一种直接运行且带原任务参数
        } catch (SchedulerException e) {
            log.error("立即运行一次定时任务失败", e);
        }
    }

    @Transactional
    public void deleteJob(JobCmd cmd) {
        try {
            //暂停、移除、删除
            String jobName = cmd.getName();
            String groupName = cmd.getGroup();
            scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, groupName));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, groupName));
            scheduler.deleteJob(JobKey.jobKey(jobName, groupName));
            // todo 删除历史记录
        } catch (Exception e) {
            log.error("删除任务失败", e);
        }
    }


    public void startAllJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            log.error("开启所有的任务失败", e);
        }
    }


    public void pauseAllJobs() {
        try {
            scheduler.pauseAll();
        } catch (Exception e) {
            log.error("暂停所有任务失败", e);
        }
    }


    public void resumeAllJobs() {
        try {
            scheduler.resumeAll();
        } catch (Exception e) {
            log.error("恢复所有任务失败", e);
        }
    }


    public void shutdownAllJobs() {
        try {

            if (!scheduler.isShutdown()) {
                // 需谨慎操作关闭scheduler容器
                // scheduler生命周期结束，无法再 start() 启动scheduler
                scheduler.shutdown(true);
            }
        } catch (Exception e) {
            log.error("关闭所有的任务失败", e);
        }
    }


}